﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.AddIn;
using Common;
using System.Web.Script.Serialization;
using System.Net;
using System.IO;

namespace AppMSR1
{
    [AddIn("AppMSR1")]
    public class AppMSR1 : Common.ModuleBase
    {
        View.VPort sensorPort;
        View.VCapability sensorPortCapability;
        View.VPort meterPort;
        View.VCapability meterPortCapability;

        bool moduleStopped = false;

        byte sensorValue = 0;
        Int32 meterValue = 0;

        Int32 timercounter = 0;
        System.Threading.Timer timer;

        /*
        public struct PushDataHeaderType
        {
            public String researchId;
            public String groupId;
            public String srcId;
            public String deviceType;
            public String deviceId;
            public String location;
            public String recordTime;

            public PushDataHeaderType(String researchId, String groupId, String srcId, String deviceType, String deviceId, String location, String recordTime)
            {
                this.researchId = researchId;
                this.groupId = groupId;
                this.srcId = srcId;
                this.deviceType = deviceType;
                this.deviceId = deviceId;
                this.location = location;
                this.recordTime = recordTime;
            }
        }

        public struct BasicData
        {
            public PushDataHeaderType header;
            public String value;
            public BasicData(PushDataHeaderType header, String value)
            {
                this.header = header;
                this.value = value;
            }
        }
        */
        public struct BasicDataType
        {
            public String researchId;
            public String groupId;
            public String srcId;
            public String deviceType;
            public String deviceId;
            public String location;
            public String recordTime;
            public String value;
            public BasicDataType(String researchId, String groupId, String srcId, String deviceType, String deviceId, String location, String recordTime, String value)
            {
                this.researchId = researchId;
                this.groupId = groupId;
                this.srcId = srcId;
                this.deviceType = deviceType;
                this.deviceId = deviceId;
                this.location = location;
                this.recordTime = recordTime;
                this.value = value;
            }
        }
        List<BasicDataType> listBasicData = new List<BasicDataType>();

        public BasicDataType basicData = new BasicDataType("Ucl", "home", "12311", "", "f01", "London", "", "");

        public override void Start()
        {
            logger.Log("Started: {0}", ToString());

            //..... get the list of current ports from the platform
            IList<View.VPort> allPortsList = GetAllPortsFromPlatform();

            if (allPortsList != null)
            {
                foreach (View.VPort port in allPortsList)
                {
                    PortRegistered(port);
                }
            }

            timer = new System.Threading.Timer(TimerMain);
            timer.Change(0, 1000 * 1); //Starts timer, TimerMain() will be called every 1 seccond
        }

        private void TimerMain(Object state)
        {
            timercounter++;
            
            if (timercounter % 20 == 0)
                SendStoredDataAll();
        }

        public enum SensorTypes
        {
            SENSOR_TYPE_TEMPURATURE = 1,
            SENSOR_TYPE_LUMINANCE = 3,
            SENSOR_TYPE_HUMIDITY = 5
        }

        private void GetValueFromSensor()
        {
            if (sensorPort != null && sensorPortCapability == null)
            {
                sensorPortCapability = GetCapability(sensorPort, Globals.UserSystem);
            }

            if (sensorPort != null && sensorPortCapability != null)
            {
                IList<View.VParamType> retVals = sensorPort.Invoke(RoleSensor.RoleName, RoleSensor.OpGetName, new List<View.VParamType>(),
                                                                  ControlPort, sensorPortCapability, ControlPortCapability);

                if (retVals[0].Maintype() != (int)ParamType.SimpleType.error)
                {
                    logger.Log("{0} successfully received value", this.ToString());

                    if (retVals.Count >= 1 && retVals[0].Value() != null)
                    {
                        sensorValue = (byte)retVals[0].Value();
                        return;
                    }
                    else
                    {
                        logger.Log("{0} got null value", this.ToString());
                        sensorValue = 2;
                        return;
                    }
                }
                else
                {
                    logger.Log("{0} failed to get value response", this.ToString());
                    sensorValue = 2;
                    return;
                }
            }
            else
            {
                logger.Log("{0} either cannot find sensor port or cannot obtain capability", this.ToString());
                sensorValue = 2;
                return;
            }
        }

        private void GetValueFromMeter()
        {
            if (sensorPort != null && meterPortCapability == null)
            {
                meterPortCapability = GetCapability(meterPort, Globals.UserSystem);
            }

            if (meterPort != null && meterPortCapability != null)
            {
                IList<View.VParamType> retVals = meterPort.Invoke(RoleMeter.RoleName, RoleMeter.OpGetMeter, new List<View.VParamType>(),
                                                                  ControlPort, meterPortCapability, ControlPortCapability);


                if (retVals[0].Maintype() != (int)ParamType.SimpleType.error)
                {
                    logger.Log("{0} successfully received value", this.ToString());

                    if (retVals.Count >= 1 && retVals[0].Value() != null)
                    {
                        meterValue = (Int32)retVals[0].Value();
                        return;
                    }
                    else
                    {
                        logger.Log("{0} got null value", this.ToString());
                        meterValue = -1;
                        return;
                    }
                }
                else
                {
                    logger.Log("{0} failed to get value response", this.ToString());
                    meterValue = -1;
                    return;
                }
            }
            else
            {
                logger.Log("{0} either cannot find meter port or cannot obtain capability", this.ToString());
                meterValue = -1;
                return;
            }
        }

        private void SendStoredDataAll()
        {
            //if it connected with server, send stored data 
            if (GetServerStatus())
            {
                if (listBasicData.Count >= 1)
                {
                    //logging
                    logger.Log("{0} Send BasicData to server", this.ToString());
                    logger.Log("================================================================");
                    foreach (BasicDataType ob in listBasicData)
                    {
                        logger.Log("The {0} is {1} at {2}", ob.deviceType, ob.value.ToString(), ob.recordTime);
                    }
                    logger.Log("================================================================");

                    if (sendDataToServer(listBasicData))
                        listBasicData = new List<BasicDataType>();
                }
                else
                {
                    logger.Log("{0} No BasicData data to send", this.ToString());
                }
            }
            else
            {
                logger.Log("{0} Server doesn't respond", this.ToString());
            }
        }

        private bool sendDataToServer (object listTypeData)
        {
           // bool ret = false;

            HttpWebRequest objRequest = (HttpWebRequest)WebRequest.Create("http://localhost:9999/msr-ws/api/msr/reading/submit/");
            objRequest.Method = "POST";
            objRequest.ContentType = "application/json";
            string jsonString = listTypeData.ToJSON();
            jsonString = "{\"readings\":" + jsonString + "}";
            //jsonString = "{\"readings\":[{\"researchId\":\"Ucl\",\"groupId\":\"home\",\"srcId\":\"2222\",\"deviceType\":\"lights\",\"deviceId\":\"e1\",\"location\":\"kitchen\",\"recordTime\":\"29/08/2013 23:28:00\",\"value\":\"255\"}]}";
            objRequest.ContentLength = jsonString.Length;

            logger.Log("Converted JSON : {0}", jsonString);
            
            StreamWriter requestStream = new StreamWriter(objRequest.GetRequestStream());
            requestStream.Write(jsonString);
            requestStream.Close();

            HttpWebResponse result = (HttpWebResponse)objRequest.GetResponse();

            if (result.StatusCode == HttpStatusCode.Created)
            {
                //StreamReader reqStreamReader = new StreamReader(result.GetResponseStream(), Encoding.GetEncoding("utf-8"));
                //string strResult = reqStreamReader.ReadToEnd();

                //if (strResult == "OK something")
                //{
                //    logger.Log("{0} Send Door Sensor data to server : Success", this.ToString());
                //    ret = true;
                //}
                //else 
                //{
                //    ret = false;
                //}
                logger.Log("{0} Send Door Sensor data to server : Success", this.ToString());
                objRequest.Abort();
                //reqStreamReader.Close();
                //return ret;               

                return true;
            }
            else
            {
                logger.Log("{0} Send Door Sensor data to server : Fail", this.ToString());
                return false;
            }
        }

        private bool GetServerStatus()
        {
            bool status = false;

            logger.Log("{0} check server status", this.ToString());
            //Todo
            //need to add an API for status

            if (status)
                return true;
            else
                return true; //false
        }

        public override void Stop()
        {
            lock (this)
            {
                timer.Dispose();
                moduleStopped = true;
            }
        }

        public override void AsyncReturn(string roleName, string opName, IList<View.VParamType> retVals, View.VPort senderPort)
        {
            basicData.recordTime = DateTime.Now.ToString("dd\\/MM\\/yyyy HH:mm:ss");

            if (retVals.Count >= 1 && retVals[0].Value() != null)
            {
                if (RoleSensor.OpGetName.Equals(opName, StringComparison.CurrentCultureIgnoreCase))
                {
                    if ((byte)retVals[0].Value() == 04) //Hardcoded node id
                    {
                        basicData.deviceType = "door";
                        if ((byte)retVals[1].Value() == 0)
                            basicData.value = "0";
                        else
                            basicData.value = "1";
                    }
                    else if ((byte)retVals[0].Value() == 06) //Hardcoded node id
                    {
                        basicData.deviceType = "motion";
                        basicData.value = retVals[1].Value().ToString();
                    }

                }
                else if (RoleSensor.OpGetMulti.Equals(opName, StringComparison.CurrentCultureIgnoreCase))
                {
                    byte deviceTypeValue = (byte)retVals[0].Value();
                    
                    if ((SensorTypes)deviceTypeValue == SensorTypes.SENSOR_TYPE_TEMPURATURE)
                        basicData.deviceType = "Tempurature";
                    else if ((SensorTypes)deviceTypeValue == SensorTypes.SENSOR_TYPE_LUMINANCE)
                        basicData.deviceType = "Luminance";
                    else if ((SensorTypes)deviceTypeValue == SensorTypes.SENSOR_TYPE_HUMIDITY)
                        basicData.deviceType = "Humidity";

                    basicData.value = retVals[1].Value().ToString();
                }

                listBasicData.Add(basicData);
                logger.Log("=========================================================");
                logger.Log("{0}: AsyncReturn for {1} sensor => {2} at {3} ", ToString(), basicData.deviceType, basicData.value, basicData.recordTime);
                logger.Log("=========================================================");
            }
            else
            {
                logger.Log("{0}: got unexpected retvals.Count is [{1}] from {2}", ToString(), retVals.Count.ToString(), senderPort.ToString());
            }
        }


        /// <summary>
        ///  Called when a new port is registered with the platform
        /// </summary>
        /// <param name="port"></param>
        public override void PortRegistered(View.VPort port)
        {
            lock (this)
            {
                if (Role.ContainsRole(port, RoleSensor.RoleName))
                {
                    sensorPort = port;
                    logger.Log("{0} added sensor port {1}", this.ToString(), port.ToString());
                    sensorPortCapability = GetCapability(port, Globals.UserSystem);

                    if (sensorPortCapability != null)
                    {
                        sensorPort.Subscribe(RoleSensor.RoleName, RoleSensor.OpGetName, this.ControlPort, sensorPortCapability, this.ControlPortCapability);
                        sensorPort.Subscribe(RoleSensor.RoleName, RoleSensor.OpGetMulti, this.ControlPort, sensorPortCapability, this.ControlPortCapability);
                    }
                }
                
                if (Role.ContainsRole(port, RoleMeter.RoleName))
                {
                    sensorPort = port;
                    logger.Log("{0} added meter port {1}", this.ToString(), port.ToString());
                }
            }
        }

        /// <summary>
        ///  Called when a new port is registered with the platform
        /// </summary>
        /// <param name="port"></param>
        public override void PortDeregistered(View.VPort port)
        {
            lock (this)
            {
                if (port.Equals(sensorPort))
                {
                    sensorPort = null;
                    logger.Log("{0} removed sensor port {1}", this.ToString(), port.ToString());
                }

                if (port.Equals(meterPort))
                {
                    meterPort = null;
                    logger.Log("{0} removed meter port {1}", this.ToString(), port.ToString());
                }
            }
        }

        public void Log(string format, params string[] args)
        {
            logger.Log(format, args);
        }

        internal void WindowClosed()
        {
            lock (this)
            {
                //if moduleStopped is true, we must have come here because Stop() was called by the platform
                //in that case, we do not need to do anything else
                if (!moduleStopped)
                {
                    Stop();
                    Finished();
                }
            }
        }
    }
    /*
    public static class PostDataForm
    {
        public String researchId;
        public String groupId;
        public String srcId;
        public String deviceType;
        public String deviceId;
        public String location;
        public String recordTime;

        public struct DoorSensorData
        {
            PushDataHeader header;
            public byte status; //FF:open 00:close
        }

        public struct MotionSensorData
        {
            PushDataHeader header;
            public byte status; //FF:motion 00:no-motion
        }

        public struct EtcSensorData
        {
            PushDataHeader header;
            public byte type; //01:Temperature, 03:Luminance, 05:Humidity
            public short value; //Temperature from -20 to 50, Luminance from 0 to 1000, Humidity from 20 to 90
        }
    }
    */
    public static class JSONMaker
    {
        public static string ToJSON(this object obj)
        {
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            return serializer.Serialize(obj);
        }

        public static string ToJSON(this object obj, int recursionDepth)
        {
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            serializer.RecursionLimit = recursionDepth;
            return serializer.Serialize(obj);
        }
    }
}
